package cn.rengy.web.framework.wxmp.token;
import java.time.Duration;

import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

import cn.rengy.tool.core.util.ExceptionPrintUtils;
import cn.rengy.tpa.wxmp.exception.WeChatException;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class AbstractTokenRefreshService implements TokenKeyService{
	
	@Autowired  
	private StringRedisTemplate stringRedisTemplate;
	@Autowired
	private RedissonClient redissonClient;
	
	/**
	 * 刷新Token
	 * @param wx_account_id
	 * @throws WeChatException
	 */
	public void refresh(String appid) throws WeChatException{
		/**
		 * 逻辑约定了当执行获取token的时候，redis中的token肯定为空， （1第一次使用2失效过期3返回40001先删除）
		 * 
		 * 所以当获取到锁后只要判断token不为空就可以退出，不执行获取
		 */
		//使用redislock分布式锁防止并发执行刷新，必须同时结合token为空判断，之前的方案是判断时间是否最近更新也是可以的
		RLock lock=redissonClient.getLock(getLockKey(appid));
		try {
			lock.lock();
			//判断token是否为空
			String key=getAccessKey(appid);
			String token=stringRedisTemplate.opsForValue().get(key);
			if(StringUtils.isBlank(token)){
				TokenDTO tokenDTO=newToken(appid);
				saveToken(key,tokenDTO);
				//更新access_token后ticket是否有效？无效则删除jsapiticket
				//tokenRedisService.evictJsApiTicket(wx_account_id);
			}
		}catch(WeChatException e) {
			throw e;
		}catch(Exception e) {
			log.error(ExceptionPrintUtils.getTrace(e));
		} finally {
		   lock.unlock();
		}
	}
	@Getter
	@Setter
	class TokenDTO{
		private String token;
		private int expires;
	}
	
	/**
	 * 获取一个新的token
	 * @param accessToken
	 * @return
	 */
	abstract TokenDTO newToken(String appid);
	
	void saveToken(String key,TokenDTO tokenDTO) {
		//加入缓存 提前120秒刷新
		stringRedisTemplate.opsForValue().set(key, tokenDTO.getToken(),Duration.ofSeconds(tokenDTO.getExpires()-120));
	}
	
	abstract void afterSaveToken();
}
